package erfgame.core.graphics.renderer;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.util.Arrays;
import java.util.List;


import erfgame.core.Rotation;
import erfgame.core.event.Action;
import erfgame.core.event.Event;
import erfgame.core.event.EventQueue;
import erfgame.core.world.World;
import erfgame.core.world.WorldAwarePrototype;

public class AnimatedEntityRenderer extends AbstractEntityRenderer 
	implements WorldAwarePrototype<AnimatedEntityRenderer>, Cloneable, Action {
	
	private int totalCycleTime;
	private int[] cumulativeAnimationTimes;
	private int[] animationTimes;
	protected EntityRenderer[] renderers;
	
	protected int currentIndex;
	private EntityRenderer currentRenderer;

	private World world;
	private EventQueue eventQueue;
	private Event nextEvent;
	
	public AnimatedEntityRenderer() {
		
	}
	
	public AnimatedEntityRenderer( List<AnimationFrame> frames ) {
		this.cumulativeAnimationTimes = new int[ frames.size() ];
		this.animationTimes = new int[ frames.size() ];
		this.renderers = new EntityRenderer[ frames.size() ];
		for( int i=0; i<frames.size(); i++ ) {
			AnimationFrame frame = frames.get( i );
			int duration = frame.getDuration();
			totalCycleTime += duration;
			cumulativeAnimationTimes[i] = totalCycleTime;
			renderers[i] = frame.getRenderer();
			animationTimes[i] = duration;
		}
		if( renderers.length > 0 ) {
			currentRenderer = renderers[0];
		}
	}
	
	public void setWorld(World world) {
		this.world = world;
		this.eventQueue = world.getEventQueue();
	}


	@Override
    public int getDepth(Rotation rotation) {
	    return currentRenderer.getDepth(rotation);
    }

    @Override
    public Dimension getDimension() {
        return currentRenderer.getDimension();
    }

    @Override
    public Point getGrabPoint() {
        return currentRenderer.getGrabPoint();
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public int resetCurrentIndex() {
		return updateCurrentIndex( 0 );
	}
	
	public int incrementCurrentIndex() {
		return updateCurrentIndex( (currentIndex+1)%renderers.length );
	}
	
	public int updateRunDuration( int runDuration ) {
		runDuration = runDuration % totalCycleTime;
		int index = Arrays.binarySearch( cumulativeAnimationTimes, runDuration );
		if( index < 0 ) {
			index = -(index+1);
		} else {
			index = (index+1) % renderers.length;
		}
		// adjust duration to account for partial results
		int cumulativeDuration = cumulativeAnimationTimes[index];
		return updateCurrentIndex( index ) - (cumulativeDuration - runDuration);
	}
	
	public int updateCurrentIndex( int currentIndex ) {
		this.currentIndex = currentIndex;
		currentRenderer = renderers[currentIndex];
		this.fireUpdate( RendererUpdateType.content, null );
		return animationTimes[currentIndex]; 
	}

	public void render(Graphics g, int x, int y) {
		currentRenderer.render(g, x, y);
	}

	public AnimatedEntityRenderer spawn( World world ) {
		try {
			AnimatedEntityRenderer clone = (AnimatedEntityRenderer)clone();
			clone.setWorld( world );
			return clone;
		} catch (CloneNotSupportedException e) {
			throw new RuntimeException( "this shouldn't happen", e );
		}
	}

	public void start() {
		queueEvent(animationTimes[currentIndex]);
	}
	
	public void stop() {
		if( this.nextEvent != null ) {
			this.eventQueue.removeEvent(this.nextEvent);
			this.nextEvent = null;
		}
	}

	public void perform() {
		int eventTime = incrementCurrentIndex();
		queueEvent(eventTime);
	}
	
	private void queueEvent(int eventTime) {
		nextEvent = new Event( this.world.getWorldTime() + eventTime, this );
		this.eventQueue.addEvent(nextEvent);
	}

	@Override
	public String toString() {
		return getClass().getSimpleName()+"{entity="+getEntity()+",index="+this.currentIndex+"}";
	}
	
	
}
